﻿// ============================================================================
// FileName: SIPPorxyConsole.cs
//
// Description:
// Main console display for the demonstration SIP Proxy.
//
// Author(s):
// Aaron Clauson
//
// History:
// 01 Aug 2008	Aaron Clauson	Created.
// 23 Aug 2008  Aaron Clauson   Added NAT keep-alive listener option.
//
// License: 
// This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
//
// Copyright (c) 2008 Aaron Clauson (aaronc@blueface.ie), Blue Face Ltd, Dublin, Ireland (www.blueface.ie)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that 
// the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
// disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd. 
// nor the names of its contributors may be used to endorse or promote products derived from this software without specific 
// prior written permission. 
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
// ============================================================================

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Xml;
using BlueFace.Sys.Net;
using BlueFace.VoIP.Net.SIP;
using BlueFace.VoIP.SIPServer;
using log4net;

namespace SIPSwitch.SIPProxy
{
    class SIPProxyConsole
    {
        private static ILog logger = SIPProxyState.logger;

        private static XmlNode SIPSocketsNode = SIPProxyState.SIPSocketsNode;           // An XML node that holds a the sockets the SIP Proxy porcess should listen on.
        private static string m_proxyScriptPath = SIPProxyState.ProxyScriptPath;        // The path to the Ruby script file that controls the Proxy runtime processing.
        private static int m_monitorEventPort = SIPProxyState.MonitorEventPort;         // An optional setting that if specified holds a loopback port number to send events to for a separate monitoring process.
        private static string m_natKeepAliveSocket = SIPProxyState.NATKeepAliveSocket;  // An optional setting to create a socket to listen for requests to send NAT keep-alives on one of the proxy's SIP sockets.

        private static ProxyMonitorChannel m_monitorChannel;    // If the monitor event port is specified this channel will be created to send the events.                     
        private static IPEndPoint m_natKeepAliveEndPoint;
        private static NATKeepAliveRelay m_natKeepAliveRelay;   // If the NAT keep-alive socket is specificied the NAT keep-alive sender will be created using this object.

        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("Demonstration SIP Proxy for MySIPSwitch SIP Stack (www.mysipswitch.com).");

                // Pre-flight checks.
                if(!File.Exists(m_proxyScriptPath))
                {
                    throw new ApplicationException("The proxy cannot start without a script. Path " + m_proxyScriptPath + " could not be loaded.");
                }
                else if(SIPSocketsNode == null || SIPSocketsNode.ChildNodes.Count == 0)
                {
                    throw new ApplicationException("The proxy cannot start without at least one socket specified to listen on, please check config file.");
                }

                // Initialise the SIP Transport layer and set up all the socket listeners.
                SIPTransport sipTransport = new SIPTransport(null, false, false);
                foreach (XmlNode sipSocketNode in SIPSocketsNode.ChildNodes)
                {
                    SIPUDPChannel udpChannel = new SIPUDPChannel(IPSocket.ParseSocketString(sipSocketNode.InnerText));
                    sipTransport.AddSIPChannel(udpChannel);
                }

                #region Optionally enable monitoring and event logging.

                // The monitor channel is completely optional and whether it is specified or not will not change the proxy's behaviour.
                // Send events from this process to the monitoring socket.
                if (m_monitorEventPort != 0)
                {
                    // Events will be sent by the monitor channel to the loopback interface and this port.
                    m_monitorChannel = new ProxyMonitorChannel(m_monitorEventPort);
                    logger.Debug("Monitor channel initialised for 127.0.0.1:" + m_monitorEventPort + ".");
                }

                sipTransport.SIPRequestInTraceEvent += (protocol, localEndPoint, endPoint, sipRequest) => ProxyLog(new ProxyMonitorEvent(ProxyServerTypesEnum.StatelessProxy, ProxyEventTypesEnum.FullSIPTrace, null, sipRequest, null, localEndPoint, endPoint, SIPCallDirection.In));
                sipTransport.SIPRequestOutTraceEvent += (protocol, localEndPoint, endPoint, sipRequest) => ProxyLog(new ProxyMonitorEvent(ProxyServerTypesEnum.StatelessProxy, ProxyEventTypesEnum.FullSIPTrace, null, sipRequest, null, localEndPoint, endPoint, SIPCallDirection.Out));
                sipTransport.SIPResponseInTraceEvent += (protocol, localEndPoint, endPoint, sipResponse) => ProxyLog(new ProxyMonitorEvent(ProxyServerTypesEnum.StatelessProxy, ProxyEventTypesEnum.FullSIPTrace, null, null, sipResponse, localEndPoint, endPoint, SIPCallDirection.In));
                sipTransport.SIPResponseOutTraceEvent += (protocol, localEndPoint, endPoint, sipResponse) => ProxyLog(new ProxyMonitorEvent(ProxyServerTypesEnum.StatelessProxy, ProxyEventTypesEnum.FullSIPTrace, null, null, sipResponse, localEndPoint, endPoint, SIPCallDirection.Out));

                #endregion

                #region Optionally initialise the NAT keep-alive multiplexer.

                if (m_natKeepAliveSocket != null)
                {
                    m_natKeepAliveEndPoint = IPSocket.ParseSocketString(m_natKeepAliveSocket);
                    if (m_natKeepAliveEndPoint != null)
                    {
                        m_natKeepAliveRelay = new NATKeepAliveRelay(sipTransport, m_natKeepAliveEndPoint, ProxyLog);
                        logger.Debug("NAT keep-alive realy created on " + m_natKeepAliveEndPoint + ".");
                    }
                    else
                    {
                        logger.Warn("The Proxy's NAT keep-alive relay was not enabled as the socket could not be parsed from " + m_natKeepAliveSocket + ".");
                    }
                }

                #endregion

                StatelessProxyCore proxyCore = new StatelessProxyCore(
                    logEvent => ProxyLog(logEvent),
                    sipTransport,
                    m_proxyScriptPath);

                // Events that pass the SIP requests and responses onto the Stateless Proxy Core.
                sipTransport.SIPTransportRequestReceived += new SIPTransportRequestReceivedDelegate(proxyCore.GotRequest);
                sipTransport.SIPTransportResponseReceived += new SIPTransportResponseReceivedDelegate(proxyCore.GotResponse);

                Console.WriteLine("SIP Proxy running, ctrl-c to exit...");

                ManualResetEvent stayAliveEvent = new ManualResetEvent(false);
                stayAliveEvent.WaitOne();

                // Normally the sockets would be gracefully shutdown but with a ctrl-c exit the OS will reclaim everything from the killed process.
                //sipTransport.Shutdown();
            }
            catch (Exception excp)
            {
                Console.WriteLine("Exception Main. " + excp.Message);
                Console.WriteLine("press any key to exit...");
                Console.ReadLine();
            }
        }

        private static void ProxyLog(ProxyMonitorEvent monitorEvent)
        {
            try
            {
                if (monitorEvent.EventType != ProxyEventTypesEnum.FullSIPTrace && monitorEvent.EventType != ProxyEventTypesEnum.NATKeepAliveRelay)
                {
                    logger.Debug(monitorEvent.Message);
                }

                // If the monitor channel has been configured send the event.
                if (m_monitorChannel != null)
                {
                    m_monitorChannel.Send(monitorEvent);
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception ProxyLog. " + excp.Message);
            }
        }
    }
}
